#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <include/pf_arg.h>
#include <include/pf_internal.h>

#include "uthash.h"

#define MAX_KEY_LEN     1024
#define MAX_VAL_LEN     128 * 1024

typedef struct pf_arg_kv
{
    UT_hash_handle  hh;
    char*           value;
    char            key[0];  /* Actually data in key[] is : |key|\0|value|\0| */
}pf_arg_kv_t;

struct pf_arg
{
    pf_arg_kv_t*    hash_table;
    char*           exec;
};

pf_arg_t* pf_arg_parse(int argc, char** argv)
{
    int i                    = 0;
    char key[MAX_KEY_LEN];
    char value[MAX_VAL_LEN];
    int key_len              = 0;
    int value_len            = 0;
    pf_arg_kv_t* kv          = NULL;

    pf_arg_t* arg = malloc(sizeof(pf_arg_t));
    if (arg == NULL)
    {
        pf_set_errno(PF_ENOMEM);
        return NULL;
    }
    memset(arg, 0x00, sizeof(pf_arg_t));

    arg->exec = malloc(strlen(argv[0]) + 1);
    if (arg->exec == NULL)
    {
        pf_set_errno(PF_ENOMEM);
        free(arg);
        return NULL;
    }

    strcpy(arg->exec, argv[0]);

    for (i = 1; i < argc; i++)
    {
        key[0] = '\0';
        value[0] = '\0';
        sscanf(argv[i], "%*[-]%[^=]=%s", key, value);
        key[MAX_KEY_LEN - 1] = '\0';
        value[MAX_VAL_LEN - 1] = '\0';
        key_len = strlen(key);
        value_len = strlen(value);

        kv = malloc(sizeof(pf_arg_kv_t) + key_len + value_len + 2);
        if (kv == NULL)
        {
            pf_arg_clear(arg);
            pf_set_errno(PF_ENOMEM);
            return NULL;
        }

        strcpy(kv->key, key);
        kv->key[key_len] = '\0';
        kv->value = &kv->key[key_len + 1];
        strcpy(kv->value, value);
        kv->key[key_len + 1 + value_len] = '\0';

        HASH_ADD_STR(arg->hash_table, key, kv);
    }

    return arg;
}

const char* pf_arg_find(const pf_arg_t* arg, const char* name)
{
    pf_arg_kv_t* kv = NULL;

    if ((arg == NULL) || (name == NULL))
    {
        pf_set_errno(PF_EINVAL);
        return NULL;
    }

    HASH_FIND_STR(arg->hash_table, name, kv);
    if (kv == NULL)
    {
        pf_set_errno(PF_ENOKEY);
        return NULL;
    }

    return kv->value;
}

const char* pf_arg_exec(const pf_arg_t* arg)
{
    if (arg == NULL)
    {
        pf_set_errno(PF_EINVAL);
        return NULL;
    }

    return arg->exec;
}

int pf_arg_clear(pf_arg_t* arg)
{
    pf_arg_kv_t* cur = NULL;
    pf_arg_kv_t* tmp = NULL;

    if (arg == NULL)
    {
        pf_set_errno(PF_EINVAL);
        return PF_EINVAL;
    }

    HASH_ITER(hh, arg->hash_table, cur, tmp)
    {
        HASH_DEL(arg->hash_table, cur);
        free(cur);
    }

    free(arg->exec);
    free(arg);

    return 0;
}

